package org.kojo.tdrm.service;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.apache.tools.ant.util.DateUtils;
import org.kojo.tdrm.entity.Task;
import org.kojo.tdrm.entity.User;
import org.kojo.tdrm.entity.UserConf;
import org.kojo.tdrm.service.notic.EmailNotification;
import org.kojo.tdrm.service.notic.FBNotification;
import org.kojo.tdrm.service.notic.INotification;

public class NotificationAppService {
	private static Logger log = Logger.getLogger(NotificationAppService.class
			.getName());
	private Set<INotification> lsNotification;

	public NotificationAppService() {
		lsNotification = new HashSet<INotification>();
		lsNotification.add(new EmailNotification());
		lsNotification.add(new FBNotification());
	}
	
	public void process() {
		log.info("[app] NotificationAppService process...");
		UserAppService uas = new UserAppService();
		TaskAppService tas = new TaskAppService();
		List<User> users = uas.findAll();
		log.fine("[app] NotificationAppService for " + users.size() + " users");
		for (User user : users) { // for each user
			Set<Long> timeOutTaskIds = new HashSet<Long>();
			Set<Task> remTasks = new HashSet<Task>();
			UserConf uconf = uas.findUserConf(user.getName());
			int reminderTime = uconf.getReminderTime();
			if (reminderTime != 0) { // if reminderTime is not 0
				// query user tasks
				List<Task> tasks = tas.listTaskByState(user.getName(),
						Task.ONGOING);
				// for each tasks send notification
				for (Task task : tasks) {
					if (isTaskTimeout(task)) {
						timeOutTaskIds.add(task.getKey().getId());
					}
					// check between task and user remiderTime
					if (isMatchCondition(reminderTime, task.getDueDate())) {
						log.fine("[app] user["
								+ user.getName()
								+ "] add task to reminder("
								+ reminderTime
								+ " MIN before task) for task = "
								+ task.getName()
								+ " dueDate="
								+ DateUtils.format(task.getDueDate(),
										DateUtils.ISO8601_DATETIME_PATTERN));
						remTasks.add(task);
					}
				} // end for task
				if (remTasks.size() != 0) {
					Map<String, Object> params = new HashMap<String, Object>();
					params.put(EmailNotification.EMAIL, user.getName());
					params.put(EmailNotification.REM_TASKS, remTasks);
					params.put(FBNotification.PROFILE_ID, user.getProfileId());
					for (INotification n : lsNotification) {
						n.setParams(params);
						n.publish();
					}
				}

			} // end if
			log.fine("[app] user[" + user.getName() + "] have timeout task = "
					+ timeOutTaskIds);
			log.fine("[app] user[" + user.getName() + "] have reminder task = "
					+ remTasks.size() + " tasks.");
		} // end for user
		log.info("[app] NotificationAppService success");
	}

	/**
	 * match condition that dueDate is close to current date specify by
	 * remiderTime
	 * 
	 * @param reminderTime
	 * @param dueDate
	 * @return
	 */
	public boolean isMatchCondition(int reminderTime, Date dueDate) {
		return this.isMatchCondition(reminderTime, dueDate, new Date());
	}

	public boolean isMatchCondition(int reminderTime, Date dueDate,
			Date currentTime) {
		// if currenttime+remiderTime is after dueDate then match notification
		// condition
		Calendar currentCal = Calendar.getInstance(Locale.US);
		currentCal.setTime(currentTime);
		currentCal.add(Calendar.MINUTE, reminderTime);
		Calendar dueCal = Calendar.getInstance(Locale.US);
		dueCal.setTime(dueDate);
		if (currentCal.after(dueCal)) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isTaskTimeout(Task t) {
		Calendar current = Calendar.getInstance(Locale.US);
		Calendar dueDateCal = Calendar.getInstance(Locale.US);
		dueDateCal.setTime(t.getDueDate());
		if (current.after(dueDateCal)) {
			return true;
		} else {
			return false;
		}
	}

	public void addNotifier(INotification notifier) {
		this.lsNotification.add(notifier);
	}

}
